﻿using System;
using System.Collections.Generic;
using System.Text;
using AMStudio.Script;

namespace IPTE.Manager
{

    public enum PLC_IO_Flag
    {
        Unkown = -1,
        CIO = 0,
        DM,
        H,
        E,
        W
    }

    public enum PLC_Action_Flag
    {
        Unkown = -1,
        Read = 0,
        Write,
        Both
    }

    public enum PLC_IO_DATA_TYPE
    {
        Bit,
        Byte,
        Word,
        DWord
    }

    public class PLC_Cell
    {
        public readonly string Name;
        public readonly string ModName;
        public readonly string Port_Addr;
        public readonly string Bit_Index;
        public readonly int Data_Len;
        public readonly PLC_Action_Flag Action;
        public readonly PLC_IO_Flag Type;
        public readonly PLC_IO_DATA_TYPE Data_Type;
        public readonly int ID;
        private int _bit_index;
        public List<string> Child_Bits = new List<string>();

        public PLC_Cell(int port, int index, int len, PLC_Action_Flag action, PLC_IO_Flag type, PLC_IO_DATA_TYPE data_type, int id)
        {
            Start_Addr = port;
            Bit_Address = index;
            Port_Addr = port.ToString("D4");
            Bit_Index = index.ToString("D2");
            _bit_index = index;
            //Data_Len = len.ToString("D4");
            Data_Len = len;
            Action = action;
            Type = type;
            Data_Type = data_type;
            ID = id;


            //if ((action == PLC_Action_Flag.Read) && (data_type == PLC_IO_DATA_TYPE.Word))
            {
                _send_data = new UInt16[1];
                _send_data[0] = (UInt16)len;
            }
            this.DataValue = new byte[2 * len];
            WordValue = new int[len];
        }

        public PLC_Cell(int port, int index, int len, PLC_Action_Flag action, PLC_IO_Flag type, PLC_IO_DATA_TYPE data_type)
            : this(port, index, len, action, type, data_type, -1)
        {
            //this.DataValue = new byte[2 * len];
        }

        #region "Used by Robot Driver for old machine"
        public void ResetBuffer()
        {
            _send_data = new UInt16[1];
            _send_data[0] = (UInt16)Data_Len;
            _recv_data = null;
        }
        private UInt16[] _send_data;
        public string Send_Data
        {
            get
            {
                string data_string = string.Empty;

                if (_send_data != null)
                {
                    foreach (UInt16 var in _send_data)
                    {
                        data_string += var.ToString("D4");
                    }
                }

                return data_string;
            }
            set
            {
                string[] data_string_array = value.Split(new char[] { ',' });

                _send_data = null;
                if (data_string_array != null)
                {
                    int len = data_string_array.Length;

                    _send_data = new UInt16[len];
                    for (int i = 0; i < len; i++)
                    {
                        _send_data[i] = UInt16.Parse(data_string_array[i]);
                    }
                }
            }
        }
        private UInt16[] _recv_data;
        public string Recv_Data
        {
            get
            {
                string data_string = string.Empty;

                if (_recv_data != null)
                {
                    for (int i = 0; i < _recv_data.Length; i++)
                    {
                        data_string += _recv_data[i].ToString("D4");
                        if (i < _recv_data.Length - 1)
                        {
                            data_string += ",";
                        }
                    }
                }

                return data_string;
            }
            set
            {
                string[] data_string_array = value.Split(new char[] { ',' });

                _recv_data = null;
                if (data_string_array != null)
                {
                    int len = data_string_array.Length;

                    _recv_data = new UInt16[len];
                    for (int i = 0; i < len; i++)
                    {
                        _recv_data[i] = UInt16.Parse(data_string_array[i], System.Globalization.NumberStyles.HexNumber);
                    }
                }
            }
        }
        public int[] GetReceivedData()
        {
            int[] ret = null;

            if (_recv_data != null)
            {
                ret = new int[_recv_data.Length];

                int i = 0;
                foreach (UInt16 var in _recv_data)
                {
                    ret[i++] = var;
                }
            }
            return ret;
        }
        public int GetBitValue()
        {
            int ret = 0;

            if (Data_Type == PLC_IO_DATA_TYPE.Bit)
            {
                if (Data_Len == 1)
                {
                    if ((_recv_data != null) && (_recv_data.Length > 0))
                    {
                        //UInt16 value = UInt16.Parse(word, System.Globalization.NumberStyles.HexNumber);
                        UInt16 value = (UInt16)_recv_data[0];
                        UInt16 mark = (UInt16)(1 << _bit_index);

                        ret = ((value & mark) >> _bit_index);
                    }
                }
                else
                {
                    if ((_recv_data != null) && (_recv_data.Length > 0))
                    {
                        UInt16 value = (UInt16)_recv_data[0];
                        UInt16 mark = (UInt16)((1 << Data_Len) - 1);

                        value = (UInt16)(value >> _bit_index);
                        ret = (value & mark);
                    }
                }
            }
            return ret;
        }
        #endregion

        //below is for Fins driver.
        public readonly int Start_Addr;
        public readonly int Bit_Address;
        public readonly byte[] DataValue;
        public bool BitValue;
        public int[] WordValue;

        //below is for script support
        public readonly string ReadMode;
        public readonly int PLC;
        public bool AlreadyReadForCycle;
        public PLC_Cell(int port, int index, int len, PLC_Action_Flag action, PLC_IO_Flag type, PLC_IO_DATA_TYPE data_type, int id, string readMode, int plc)
            : this(port, index, len, action, type, data_type, id)
        {
            this.ReadMode = readMode;
            this.PLC = plc;
        }
        public PLC_Cell(string name, string modName, int port, int index, int len, PLC_Action_Flag action, PLC_IO_Flag type, PLC_IO_DATA_TYPE data_type, int id, string readMode, int plc)
            : this(port, index, len, action, type, data_type, id, readMode, plc)
        {
            this.Name = name;
            this.ModName = modName;
        }
        public void GetSubCellValue(PLC_Cell subCell)
        {
            int offset = subCell.Start_Addr - this.Start_Addr;
            subCell.WordValue = new int[subCell.Data_Len];
            Array.Copy(this.WordValue, offset, subCell.WordValue, 0, subCell.Data_Len);
            subCell.BitValue = subCell.GetSubBitBoolValue(subCell.Bit_Address);
        }
        public bool GetSubBitBoolValue(int bitPosition)
        {
            int index = bitPosition / 16;
            int bitIndex = bitPosition % 16;
            bitIndex = 1 << bitIndex;
            if ((this.WordValue[index] & bitIndex) > 0)
            {
                return true;
            }
            return false;
        }
        
    }

    public class PLC_Func
    {
        public string Name = string.Empty;
        public string Text = string.Empty;
        public string[] Bits;
        public PLC_Func(string n, string t)
        {
            Name = n;
            Text = t;
        }
    }

    public class PLCWordCell : PLC_Cell, IRefrenceValueTag<int>
    {
        private IRefrenceTagProvider provider = null;

        public IRefrenceTagProvider TagProvider
        {
            get
            {
                return this.provider;
            }
            set
            {
                this.provider = value;
            }
        }

        public int Value
        {
            get
            {
                provider.ReadTag(this);
                return this.WordValue[0];
            }
            set
            {
                this.WordValue[0] = value;
                provider.WriteTag(this);
            }
        }
        public bool[] BoolArraryValue()
        {
            bool[] temp = new bool[16];
            int data = this.Value;
            for (int i = 0; i < 16; i++)
            {
                temp[i] = (data & (1 << (i % 16))) > 0 ? true : false;
            }
            return temp;
        }
        public override string ToString()
        {
            return this.Value.ToString();
        }

        public PLCWordCell(string name, string modName, int port, int index, int len, PLC_Action_Flag action, PLC_IO_Flag type, PLC_IO_DATA_TYPE data_type, int id, string readMode, int plc)
            : base(name, modName, port, index, len, action, type, data_type, id, readMode, plc)
        {
        }
    }
    public class PLCBitCell : PLC_Cell, IRefrenceValueTag<bool>
    {
        private IRefrenceTagProvider provider = null;
        public IRefrenceTagProvider TagProvider
        {
            get
            {
                return this.provider;
            }
            set
            {
                this.provider = value;
            }
        }

        public bool Value
        {
            get
            {
                provider.ReadTag(this);
                return this.BitValue;
            }
            set
            {
                this.BitValue = value;
                provider.WriteTag(this);
            }
        }
        public override string ToString()
        {
            return this.Value.ToString();
        }

        public PLCBitCell(string name, string modName, int port, int index, int len, PLC_Action_Flag action, PLC_IO_Flag type, PLC_IO_DATA_TYPE data_type, int id, string readMode, int plc)
            : base(name, modName, port, index, len, action, type, data_type, id, readMode, plc)
        {
        }

    }
    public class PLCWordArrayCell : PLC_Cell, IRefrenceValueTag<int[]>
    {
        private IRefrenceTagProvider provider = null;

        public IRefrenceTagProvider TagProvider
        {
            get
            {
                return this.provider;
            }
            set
            {
                this.provider = value;
            }
        }

        public int[] Value
        {
            get
            {
                provider.ReadTag(this);
                return this.WordValue;
            }
            set
            {
                this.WordValue = value;
                provider.WriteTag(this);
            }
        }

        public override string ToString()
        {
            StringBuilder str = new StringBuilder();
            int[] v = this.Value;
            for (int i = 0; i < this.Data_Len; i++)
            {
                str.Append(v[i].ToString());
            }
            return str.ToString();
        }

        public PLCWordArrayCell(string name, string modName, int port, int index, int len, PLC_Action_Flag action, PLC_IO_Flag type, PLC_IO_DATA_TYPE data_type, int id, string readMode, int plc)
            : base(name, modName, port, index, len, action, type, data_type, id, readMode, plc)
        {
        }

        public bool[] BoolArraryValue()
        {
            int len = this.Data_Len * 16;
            bool[] temp = new bool[len];
            int[] data = this.Value;
            for (int i = 0; i < len; i++)
            {
                temp[i] = (data[i / 16] & (1 << (i % 16))) > 0 ? true : false;
            }
            return temp;
        }
        public int[] DWordValue
        {
            get
            {
                int myLen = Data_Len / 2;
                int[] dwords = new int[myLen];
                for (int i = 0; i < myLen; i++)
                {
                    dwords[i] = Value[i * 2] + 65536 * Value[i * 2 + 1];
                }
                return dwords;
            }
            set
            {
                int myLen = Data_Len / 2;
                for (int i = 0; i < myLen; i++)
                {
                    WordValue[i * 2] = value[i] % 65536;
                    WordValue[i * 2+1] = value[i] / 65536;
                }
                this.TagProvider.WriteTag(this); 
            }
        }
    }
}
